package ks.server.controllers.commands.admin.icon;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;

import ks.framework.common.Message;
import ks.framework.communicator.Communicator;
import ks.framework.interfaces.IProcessMessage;
import ks.managers.icon.Icon;
import ks.managers.icon.IconManager;
import ks.server.ServerContext;
import ks.server.ipc.Listener;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 * Server-side controller for processing an admin-command to add an Icon.
 * 
 * @author George Heineman
 */
public class IconAddController implements IProcessMessage {

	/** Primary agent to whom we can communicate our messages. */
	Communicator com;
	
	/** Icon directory where we will dump all icons retrieved remotely. */
	public static final String iconStorage = "Icons";
	
	/** Attributes from XML string. */
	public static final String iconAtt = "icon";
	public static final String uriAtt = "uri";
	
	/** Default controller constructor. */
	public IconAddController (Communicator com) {
		this.com = com;
	}
	
	/** 
	 * icon-add Message comes in from Client.
	 * 
	 *  <icon-add icon="someString" uri="http://www.cs.wpi.edu/~heineman/this.jpg"/>
	 */
	public boolean process(Message m) {
		Node iconAdd = m.contents();
		
		// Find out its attributes.
		NamedNodeMap nnp = iconAdd.getAttributes();
		
		Node iconNode = nnp.getNamedItem(iconAtt);
		Node uriNode = nnp.getNamedItem(uriAtt);
	
		String iconName = iconNode.getNodeValue();
		String uriValue = uriNode.getNodeValue();

		// See if we can fetch the bits from the remote URL file. If we can't stop now.
		if (!storeImage(iconName, uriValue)) {
			Listener.nack(m, 4, "Unable to retrieve image from:" + uriValue);
			return false;
		}
		
		// Ask IconManager to add the icon. Use a (now-stored) different URL
		try {
			File file = new File (iconStorage);
			URL url = new URL (file.toURI().toURL(), iconName);
			IconManager im = ServerContext.getIconManager();
			
			if (im.add(iconName, url)) {
				// successful operation!
			} else {
				Listener.nack(m, 4, "Unable to add Icon");  // low severity level
			}
		
			// Broadcast icons to everyone. Ask IconManager for collection of icons, 
			// and form this request from this set.
	        StringBuilder sb = new StringBuilder("<request version='1.0' id='1'><icons>");
	        for (Icon icon : im.getList()) {
	        	sb.append ("<icon name=\"" + icon.name + "\" uri=\"" + icon.location + "\"/>\n");
	        }
	        sb.append("</icons></request>");
	        Document d = Message.construct(sb.toString());
	        
	        // send to everyone INCLUDING the originator of the Message m.
	        Message r = new Message (d);
	        r.setRecipient(m.getOriginator());
	        r.setBroadcast();
	        
	        com.distribute(r);
	        return true;
		} catch (Exception e) {
			e.printStackTrace();

			return false;
		}
	}

	/**
	 * Try to fetch the remove image and store in our local storage.
	 * 
	 * @param iconName
	 * @param uriValue
	 * @return
	 */
	private boolean storeImage(String iconName, String uriValue) {
		URL iconBits;
		
		try {
			iconBits = new URL(uriValue);
		} catch (Exception e) {
			return false;
		}
		
		try {
			File f = new File (iconStorage, iconName);
			
			FileOutputStream fos = new FileOutputStream (f);
			
			// grab all bits from input stream and store in our output Stream
			InputStream is = iconBits.openStream();
			
			byte[] chunk = new byte[4096];
			int numRead;
			while ((numRead = is.read(chunk)) > 0) {
				fos.write(chunk, 0, numRead);
			}
			
			// done
			fos.close();
			is.close();
			
		} catch (Exception e) {
			// unable to store or create
			return false;
		}
		
		// done
		return true;
	}
}
